package com.example.demo.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@RestController
@Slf4j
public class testController {
    @Autowired
    StringRedisTemplate redisTemplate;

    private int totalNum = 10;//总商品数量

    private int lockTimeOut = 3;//锁过期时间30秒

    private int userPatient = 30000;//用户抢购的模拟时间（毫秒）

    private int userNum = 100000;//抢购人数

    @GetMapping("/monitor")
    public List<String> monitor() {
        //初始化抢购用户
        List<String> users = initUsers();
        //抢购成功用户结果表
        List<String> winners = new ArrayList<>();

        users.parallelStream().forEach(b -> {
            //用户尝试抢购
            String currentUser = rob(b);
            //如果抢购成功，则将用户放入结果表
            if (!StringUtils.isEmpty(currentUser)) {
                winners.add(currentUser);
            }
        });

        return winners;
    }


    /*
     * 初始化用户
     * <br/>
     * @param
     * @return java.util.List<java.lang.String>
     * @author Lisz
     * @date 2022/1/28 12:18
     */
    List<String> initUsers() {
        List<String> result = new ArrayList<>();
        //这里简单的用数字代指用户
        for (int i = 1; i <= userNum; i++) {
            result.add(String.valueOf(i));
        }

        return result;
    }


    @GetMapping("testLock")
    public String rob(String b) {
        //用户开抢时间
        long startTime = System.currentTimeMillis();

        //模拟用户持续抢
        while ((startTime + userPatient) >= System.currentTimeMillis()) {
            //首先查看总库存，如果为0，则返回null
            if (totalNum < 1) {
                return null;
            }

            String uuid = UUID.randomUUID().toString();
            Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", uuid, lockTimeOut, TimeUnit.SECONDS);//设置锁过期时间，防止死锁
            //获取锁成功
            if (lock) {
                //首先查看总库存，如果为0，则返回null
                if (totalNum < 1) {
                    return null;
                }
                //模拟用户生成订单时间
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                log.info("用户 {} 抢购成功", b);
                totalNum--;


                //使用LUA脚本执行原子操作，避免锁误删
                String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
                DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
                redisScript.setScriptText(script);

                // 设置一下返回值类型 为Long
                // 因为删除判断的时候，返回的0,给其封装为数据类型。如果不封装那么默认返回String 类型，
                // 那么返回字符串与0 会有发生错误。
                redisScript.setResultType(Long.class);
                // 第一个要是script 脚本 ，第二个需要判断的key，第三个就是key所对应的值。
                redisTemplate.execute(redisScript, Arrays.asList("lock"), uuid);
                return b;
            } else {
                log.error("获取锁失败！（没抢到）");
            }
        }
        return null;

    }

    @GetMapping("/test")
    public void test() {
        for (int i = 0; i < 10000; i++) {
            //testLock();
        }
    }


}
